<?php

namespace app\api\controller;

use app\common\controller\Api;
use think\Request;
use Volcengine\Common\Configuration;
use Volcengine\Ark\Api\ARKApi;
use Volcengine\Ark\Model\CreateBatchInferenceJobRequest;

/**
 * 豆包AI流式对话接口
 */
class Dou extends Api
{
    protected $noNeedLogin = ['*'];
    protected $noNeedRight = ['*'];

    private $config;
    private $apiKey;
    private $streamMode = 'char'; // 默认流式模式

    public function _initialize()
    {
        parent::_initialize();
        $this->config = config('doubao');
        $this->apiKey = $this->config['api_key'];
    }

    /**
     * 豆包AI流式对话接口
     * GET/POST /api/dou/index
     * 参数: question=你好&temperature=0.7&max_tokens=4000&stream_mode=char
     * stream_mode: char(逐字符) | word(逐词) | chunk(分块) | complete(完整)
     */
    public function index(Request $request)
    {
        try {
            // 获取参数
            $question = trim($request->param('question', ''));
            $temperature = (float)$request->param('temperature', 0.7);
            $maxTokens = (int)$request->param('max_tokens', 4000);
            $model = $request->param('model', $this->config['default_model']);
            $streamMode = $request->param('stream_mode', 'char');
            
            // 参数验证
            if (empty($question)) {
                $this->streamError('问题内容不能为空');
                return;
            }
            
            if (mb_strlen($question, 'UTF-8') > 10000) {
                $this->streamError('问题内容过长，请控制在10000字符以内');
                return;
            }
            
            if ($temperature < 0 || $temperature > 2) {
                $this->streamError('temperature参数必须在0-2之间');
                return;
            }
            
            if ($maxTokens < 1 || $maxTokens > 8000) {
                $this->streamError('max_tokens参数必须在1-8000之间');
                return;
            }
            
            // 验证流式模式
            $validModes = ['char', 'word', 'chunk', 'complete'];
            if (!in_array($streamMode, $validModes)) {
                $this->streamError('stream_mode参数必须是: ' . implode(', ', $validModes));
                return;
            }
            
            // 设置流式模式
            $this->streamMode = $streamMode;
            
            // 启动流式对话
            $this->startStreamChat($question, $temperature, $maxTokens, $model);
            
        } catch (\Exception $e) {
            error_log('豆包AI流式聊天错误: ' . $e->getMessage());
            $this->streamError('系统错误: ' . $e->getMessage());
        }
    }

    /**
     * 启动流式对话
     */
    private function startStreamChat($question, $temperature, $maxTokens, $model)
    {
        // 设置流式响应头
        $this->setOptimizedHeaders();

        try {
            $this->sendEvent('start', '开始豆包AI对话...');

            // 构建请求数据
            $requestData = [
                'model' => $model,
                'messages' => [
                    [
                        'role' => 'user',
                        'content' => $question
                    ]
                ],
                'temperature' => $temperature,
                'max_tokens' => $maxTokens,
                'stream' => true,
                'top_p' => 0.9
            ];

            // 使用SDK执行流式请求
            $this->executeRequestWithSDK($requestData);

        } catch (\Exception $e) {
            error_log('流式对话启动错误: ' . $e->getMessage());
            $this->sendEvent('error', '启动对话失败: ' . $e->getMessage());
        }

        exit;
    }

    private function executeRequestWithSDK($data)
    {
        $config = Configuration::getDefaultConfiguration()
            ->setAk($this->apiKey)
            ->setSk($this->apiKey) // 假设apiKey同时作为SK
            ->setRegion("cn-beijing");

        $apiInstance = new ARKapi(new \GuzzleHttp\Client(), $config);
        $request = new CreateBatchInferenceJobRequest();
        $request->setModel($data['model']);
        $request->setMessages($data['messages']);
        $request->setTemperature($data['temperature']);
        $request->setMaxTokens($data['max_tokens']);
        $request->setStream($data['stream']);
        $request->setTopP($data['top_p']);

        try {
            $result = $apiInstance->createBatchInferenceJob($request);
            foreach ($result->getChoices() as $choice) {
                $this->sendEvent('content', $choice->getDelta()->getContent());
            }
        } catch (\Exception $e) {
            error_log('豆包API请求错误: ' . $e->getMessage());
            $this->sendEvent('error', '请求失败: ' . $e->getMessage());
        }
    }

    /**
     * 验证请求数据
     */
    private function validateRequestData($requestData)
    {
        if (empty($requestData['messages'])) {
            throw new \Exception('消息内容不能为空');
        }
        
        if (empty($requestData['model'])) {
            throw new \Exception('模型参数不能为空');
        }
        
        // 检查消息长度
        $totalLength = 0;
        foreach ($requestData['messages'] as $message) {
            if (isset($message['content']) && is_string($message['content'])) {
                $totalLength += mb_strlen($message['content'], 'UTF-8');
            }
        }
        
        if ($totalLength > 50000) {
            throw new \Exception('消息内容过长，请减少内容后重试');
        }
    }

    /**
     * 执行流式API请求
     */
    private function executeRequest($data)
    {
        // 记录请求开始
        error_log('豆包API请求开始: ' . json_encode([
            'url' => $this->config['chat_api_url'],
            'model' => $data['model'] ?? 'unknown',
            'stream_mode' => $this->streamMode
        ]));
        
        $ch = curl_init();
        
        // 豆包API优化配置
        curl_setopt_array($ch, [
            CURLOPT_URL => $this->config['chat_api_url'],
            CURLOPT_RETURNTRANSFER => false,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($data, JSON_UNESCAPED_UNICODE),
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json; charset=utf-8',
                'Authorization: Bearer ' . $this->apiKey,
                'Accept: text/event-stream',
                'Cache-Control: no-cache',
                'Connection: keep-alive'
            ],
            CURLOPT_WRITEFUNCTION => [$this, 'handleStreamData'],
            CURLOPT_TIMEOUT => 300,  // 5分钟超时
            CURLOPT_CONNECTTIMEOUT => 30,  // 连接超时30秒
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS => 3,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_USERAGENT => 'DouAI/1.0 PHP/' . PHP_VERSION,
            // 流式优化
            CURLOPT_BUFFERSIZE => 1024,  // 1KB缓冲区
            CURLOPT_TCP_NODELAY => true,  // 禁用Nagle算法，提高实时性
            CURLOPT_LOW_SPEED_LIMIT => 1,  // 最低速度1字节/秒
            CURLOPT_LOW_SPEED_TIME => 60   // 60秒内速度低于限制则超时
        ]);
        
        $result = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        
        // 记录请求结果
        error_log('豆包API请求结束: ' . json_encode([
            'http_code' => $httpCode,
            'curl_error' => $error,
            'result' => $result !== false ? 'success' : 'failed'
        ]));
        
        if ($result === false || !empty($error)) {
            $this->sendEvent('error', '连接错误: ' . ($error ?: '未知错误'));
        } elseif ($httpCode !== 200) {
            $errorMsg = $this->getHttpErrorMessage($httpCode);
            $this->sendEvent('error', "HTTP错误 $httpCode: $errorMsg");
        }
        
        curl_close($ch);
    }

    /**
     * 处理流式数据
     */
    public function handleStreamData($ch, $data)
    {
        static $buffer = '';
        static $completeContent = '';
        static $messageCount = 0;
        static $totalDataReceived = 0;
        
        $totalDataReceived += strlen($data);
        $buffer .= $data;
        
        // 记录数据接收情况
        if ($messageCount % 10 === 0) {
            error_log("豆包API数据接收: 消息数={$messageCount}, 总数据量={$totalDataReceived}字节");
        }
        
        // 解析SSE数据流
        $lines = explode("\n", $buffer);
        $buffer = array_pop($lines); // 保留最后一行（可能不完整）
        
        foreach ($lines as $line) {
            $line = trim($line);
            
            // 处理SSE数据行
            if (strpos($line, 'data: ') === 0) {
                $json = substr($line, 6);
                
                // 检查流结束标记
                if ($json === '[DONE]') {
                    error_log('豆包API流式结束标记接收');
                    $this->finalizeStream($completeContent);
                    break;
                }
                
                // 解析JSON数据
                $response = json_decode($json, true);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    error_log('豆包API JSON解析错误: ' . json_last_error_msg() . ', 数据: ' . $json);
                    continue;
                }
                
                // 处理豆包API响应
                if (isset($response['choices'][0]['delta']['content'])) {
                    $content = $response['choices'][0]['delta']['content'];
                    $messageCount++;
                    
                    if (!empty($content)) {
                        // 积累完整内容
                        $completeContent .= $content;
                        // 清理内容
                        $cleanContent = $this->cleanContent($content);
                        if (!empty($cleanContent)) {
                            // 根据流式模式发送内容
                            $this->sendContentByMode($cleanContent);
                        }
                    }
                }
                
                // 处理错误情况
                if (isset($response['error'])) {
                    error_log('豆包API返回错误: ' . json_encode($response['error']));
                    $this->sendEvent('error', '豆包API错误: ' . $response['error']['message']);
                    break;
                }
            }
        }
        
        return strlen($data);
    }

    /**
     * 根据流式模式发送内容
     */
    private function sendContentByMode($content)
    {
        switch ($this->streamMode) {
            case 'char':
                $this->sendContentCharByChar($content);
                break;
            case 'word':
                $this->sendContentWordByWord($content);
                break;
            case 'chunk':
                $this->sendContentAsChunk($content);
                break;
            case 'complete':
                // complete模式不在这里发送，等待最后一次性发送
                break;
            default:
                $this->sendContentCharByChar($content);
        }
    }

    /**
     * 逐字符发送内容
     */
    private function sendContentCharByChar($content)
    {
        $chars = preg_split('//u', $content, -1, PREG_SPLIT_NO_EMPTY);
        
        foreach ($chars as $char) {
            if (!empty($char)) {
                $this->sendEvent('content', $char);
                
                // 根据字符类型调整延迟
                if (preg_match('/[\x{4e00}-\x{9fff}]/u', $char)) {
                    usleep(50000); // 中文字符 50ms
                } else {
                    usleep(30000); // 英文字符 30ms
                }
            }
        }
    }

    /**
     * 逐词发送内容
     */
    private function sendContentWordByWord($content)
    {
        $words = $this->splitContentIntoWords($content);
        
        foreach ($words as $word) {
            if (!empty($word)) {
                $this->sendEvent('content', $word);
                
                // 根据词语类型调整延迟
                if (preg_match('/[\x{4e00}-\x{9fff}]/u', $word)) {
                    usleep(80000); // 中文词 80ms
                } else {
                    usleep(60000); // 英文词 60ms
                }
            }
        }
    }

    /**
     * 分块发送内容
     */
    private function sendContentAsChunk($content)
    {
        $this->sendEvent('content', $content);
        usleep(100000); // 100ms延迟
    }

    /**
     * 将内容分割成词语
     */
    private function splitContentIntoWords($content)
    {
        // 使用正则表达式分割中英文词语
        preg_match_all('/[\x{4e00}-\x{9fff}]+|[a-zA-Z]+|\d+|[^\x{4e00}-\x{9fff}a-zA-Z\d\s]+|\s+/u', $content, $matches);
        return $matches[0] ?? [];
    }

    /**
     * 清理内容
     */
    private function cleanContent($content)
    {
        // 移除控制字符，但保留换行符和制表符
        $content = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $content);
        return $content;
    }

    /**
     * 完成流处理
     */
    private function finalizeStream($completeContent)
    {
        if (!empty($completeContent)) {
            try {
                // 如果是complete模式，在这里一次性发送全部内容
                if ($this->streamMode === 'complete') {
                    $cleanCompleteContent = $this->cleanContent($completeContent);
                    $this->sendEvent('content', $cleanCompleteContent);
                }
                
                // 发送处理完成的内容
                $this->sendEvent('processed', $completeContent);
                
                // 发送统计信息
                $stats = [
                    'total_length' => mb_strlen($completeContent, 'UTF-8'),
                    'word_count' => str_word_count($completeContent),
                    'processing_time' => microtime(true),
                    'stream_mode' => $this->streamMode
                ];
                
                $this->sendEvent('stats', json_encode($stats));
                
            } catch (\Exception $e) {
                error_log('流处理完成时错误: ' . $e->getMessage());
                $this->sendEvent('processed', $completeContent);
            }
        }
        
        $this->sendEvent('end', '对话完成');
    }

    /**
     * 设置优化的响应头
     */
    private function setOptimizedHeaders()
    {
        // 禁用输出缓冲
        if (ob_get_level()) {
            ob_end_clean();
        }
        
        // 设置PHP配置
        ini_set('output_buffering', 'off');
        ini_set('zlib.output_compression', 'off');
        
        // 基本SSE头
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache');
        header('Connection: keep-alive');
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
        header('X-Accel-Buffering: no'); // 禁用Nginx缓冲
        
        // 无限制执行时间
        set_time_limit(0);
        ignore_user_abort(false);
        
        // 立即刷新输出
        if (function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
        }
        
        // 发送初始数据以建立连接
        echo "data: " . json_encode(['type' => 'init', 'message' => '连接已建立']) . "\n\n";
        
        // 启用自动刷新
        if (function_exists('apache_setenv')) {
            apache_setenv('no-gzip', '1');
        }
        
        flush();
    }

    /**
     * 发送SSE事件
     */
    private function sendEvent($type, $data)
    {
        $eventData = [
            'type' => $type,
            'data' => $data,
            'timestamp' => microtime(true)
        ];
        
        // 构建SSE格式
        $sseData = "data: " . json_encode($eventData) . "\n\n";
        
        // 发送数据
        echo $sseData;
        
        // 强制刷新输出缓冲区
        if (ob_get_level()) {
            ob_flush();
        }
        flush();
        
        // 调试记录（仅在开发环境）
        if (defined('APP_DEBUG') && APP_DEBUG) {
            error_log("SSE发送: {$type} - " . substr($data, 0, 100));
        }
    }

    /**
     * 发送错误SSE事件并结束连接
     */
    private function streamError($message, $code = 500)
    {
        $this->sendEvent('error', [
            'message' => $message,
            'code' => $code,
            'timestamp' => date('Y-m-d H:i:s')
        ]);
        
        $this->sendEvent('end', '连接因错误而结束');
        
        // 记录错误日志
        error_log("豆包API流式错误 [{$code}]: {$message}");
        
        exit;
    }
}

